{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "948ffde2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Fed-MVKM Tutorial with DHA Dataset\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "# Federated Multi-View K-Means Clustering Tutorial\n",
    "## Using the DHA (Depth-RGB-Hand Action) Dataset\n",
    "\n",
    "This notebook demonstrates how to use the Fed-MVKM algorithm for federated multi-view clustering on the DHA dataset, which contains hand action data captured using both RGB and depth sensors.\n",
    "\n",
    "**Authors:** Kristina P. Sinaga  \n",
    "**Date:** May 2024  \n",
    "**Version:** 1.0\n",
    "\n",
    "### Overview:\n",
    "1. Data Loading and Preprocessing\n",
    "2. Parameter Setup\n",
    "3. Client Data Distribution\n",
    "4. Model Training\n",
    "5. Results Analysis and Visualization\n",
    "\n",
    "### Required Files:\n",
    "- Depth_DHA.mat: Depth sensor data\n",
    "- RGB_DHA.mat: RGB camera data\n",
    "- label_DHA.mat: Ground truth labels\n",
    "- clients_MVDHA.mat: Multi-view data distributed across clients\n",
    "- clients_labelset_DHA.mat: Label sets for each client\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "## 1. Setup and Imports\n",
    "\n",
    "First, let's import all necessary libraries and set up our environment.\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"python\">\n",
    "import numpy as np\n",
    "import torch\n",
    "import scipy.io as sio\n",
    "from pathlib import Path\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "# Import our Fed-MVKM implementation\n",
    "from mvkm_ed import FedMVKMED, FedMVKMEDConfig\n",
    "from mvkm_ed.utils import MVKMEDDataProcessor, MVKMEDMetrics, MVKMEDVisualizer\n",
    "\n",
    "# Set random seed for reproducibility\n",
    "np.random.seed(42)\n",
    "torch.manual_seed(42)\n",
    "\n",
    "# Enable CUDA if available\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "print(f\"Using device: {device}\")\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "## 2. Data Loading and Preprocessing\n",
    "\n",
    "We'll now load the DHA dataset from the MAT files and preprocess it using our utility functions.\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"python\">\n",
    "def load_mat_data(data_dir: Path):\n",
    "    \"\"\"Load MAT files containing DHA dataset.\"\"\"\n",
    "    # Load raw data\n",
    "    rgb_data = sio.loadmat(data_dir / 'RGB_DHA.mat')['RGB_DHA']\n",
    "    depth_data = sio.loadmat(data_dir / 'Depth_DHA.mat')['Depth_DHA']\n",
    "    labels = sio.loadmat(data_dir / 'label_DHA.mat')['label_DHA'].ravel()\n",
    "    \n",
    "    print(f\"RGB data shape: {rgb_data.shape}\")\n",
    "    print(f\"Depth data shape: {depth_data.shape}\")\n",
    "    print(f\"Number of samples: {len(labels)}\")\n",
    "    print(f\"Number of unique classes: {len(np.unique(labels))}\")\n",
    "    \n",
    "    return rgb_data, depth_data, labels\n",
    "\n",
    "# Set data directory\n",
    "data_dir = Path(\"../data\")  # Adjust this path as needed\n",
    "\n",
    "# Load and preprocess data\n",
    "rgb_data, depth_data, labels = load_mat_data(data_dir)\n",
    "\n",
    "# Preprocess views\n",
    "processor = MVKMEDDataProcessor()\n",
    "views = processor.preprocess_views(\n",
    "    [rgb_data, depth_data],\n",
    "    scale=True,\n",
    "    normalize=True\n",
    ")\n",
    "\n",
    "print(\"\\nAfter preprocessing:\")\n",
    "for i, view in enumerate(views):\n",
    "    print(f\"View {i+1} shape: {view.shape}\")\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "## 3. Client Data Distribution\n",
    "\n",
    "Now we'll split the data across clients to simulate a federated learning scenario.\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"python\">\n",
    "def create_client_partitions(views, labels, n_clients=2, balanced=True):\n",
    "    \"\"\"Split data into client partitions.\"\"\"\n",
    "    n_samples = views[0].shape[0]\n",
    "    \n",
    "    if balanced:\n",
    "        # Create balanced partitions\n",
    "        indices = np.random.permutation(n_samples)\n",
    "        client_size = n_samples // n_clients\n",
    "    else:\n",
    "        # Create unbalanced partitions (more realistic scenario)\n",
    "        ratios = np.random.dirichlet(np.ones(n_clients))\n",
    "        client_sizes = (ratios * n_samples).astype(int)\n",
    "        client_sizes[-1] = n_samples - client_sizes[:-1].sum()  # Ensure total = n_samples\n",
    "        indices = np.random.permutation(n_samples)\n",
    "        \n",
    "    client_data = {}\n",
    "    client_labels = {}\n",
    "    start_idx = 0\n",
    "    \n",
    "    for i in range(n_clients):\n",
    "        if balanced:\n",
    "            end_idx = start_idx + client_size if i < n_clients - 1 else n_samples\n",
    "        else:\n",
    "            end_idx = start_idx + client_sizes[i]\n",
    "            \n",
    "        client_indices = indices[start_idx:end_idx]\n",
    "        client_data[i] = [view[client_indices] for view in views]\n",
    "        client_labels[i] = labels[client_indices]\n",
    "        \n",
    "        start_idx = end_idx\n",
    "        \n",
    "        print(f\"Client {i+1}:\")\n",
    "        print(f\"  Number of samples: {len(client_indices)}\")\n",
    "        print(f\"  Classes present: {np.unique(client_labels[i])}\")\n",
    "        print()\n",
    "    \n",
    "    return client_data, client_labels\n",
    "\n",
    "# Create client partitions\n",
    "n_clients = 2  # Number of clients\n",
    "client_data, client_labels = create_client_partitions(\n",
    "    views, \n",
    "    labels, \n",
    "    n_clients=n_clients,\n",
    "    balanced=False  # Use unbalanced partitions for more realistic scenario\n",
    ")\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "## 4. Model Configuration and Training\n",
    "\n",
    "Let's set up the Fed-MVKM model with appropriate parameters and train it on our distributed data.\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"python\">\n",
    "# Configure the federated model\n",
    "config = FedMVKMEDConfig(\n",
    "    cluster_num=len(np.unique(labels)),  # Number of clusters\n",
    "    points_view=len(views),              # Number of views (RGB and Depth)\n",
    "    alpha=15.0,                          # View weight control\n",
    "    beta=1.0,                            # Initial distance parameter\n",
    "    gamma=0.04,                          # Model update rate\n",
    "    max_iterations=10,                    # Maximum federation rounds\n",
    "    convergence_threshold=1e-4,          # Convergence criterion\n",
    "    random_state=42,                     # For reproducibility\n",
    "    verbose=True                         # Show progress\n",
    ")\n",
    "\n",
    "# Initialize and train the model\n",
    "model = FedMVKMED(config)\n",
    "print(\"Starting federated training...\")\n",
    "model = model.fit(client_data)\n",
    "print(\"Training complete!\")\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "## 5. Results Analysis\n",
    "\n",
    "Now let's analyze the clustering results and visualize the performance.\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"python\">\n",
    "# Get predictions for all clients\n",
    "predictions = model.predict(client_data)\n",
    "\n",
    "# Combine all predictions and true labels\n",
    "all_predictions = []\n",
    "all_true_labels = []\n",
    "\n",
    "for client_id in predictions:\n",
    "    all_predictions.append(predictions[client_id])\n",
    "    all_true_labels.append(client_labels[client_id])\n",
    "\n",
    "all_predictions = np.concatenate(all_predictions)\n",
    "all_true_labels = np.concatenate(all_true_labels)\n",
    "\n",
    "# Compute clustering metrics\n",
    "metrics = MVKMEDMetrics.compute_metrics(\n",
    "    views, \n",
    "    all_predictions,\n",
    "    all_true_labels\n",
    ")\n",
    "\n",
    "print(\"\\nClustering Performance Metrics:\")\n",
    "for metric, value in metrics.items():\n",
    "    print(f\"{metric:15s}: {value:.4f}\")\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "## 6. Visualization\n",
    "\n",
    "Let's create some visualizations to understand the model's behavior during training.\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"python\">\n",
    "# Create visualizer\n",
    "visualizer = MVKMEDVisualizer(model)\n",
    "\n",
    "# Set up the plotting area\n",
    "plt.figure(figsize=(15, 5))\n",
    "\n",
    "# Plot 1: Convergence Analysis\n",
    "plt.subplot(1, 2, 1)\n",
    "visualizer.plot_convergence()\n",
    "\n",
    "# Plot 2: View Weight Evolution\n",
    "plt.subplot(1, 2, 2)\n",
    "visualizer.plot_view_weights()\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# Plot client-specific objectives\n",
    "plt.figure(figsize=(10, 6))\n",
    "for client_id, objectives in model.history['client_objectives'].items():\n",
    "    plt.plot(objectives, label=f'Client {client_id+1}')\n",
    "plt.xlabel('Iteration')\n",
    "plt.ylabel('Objective Value')\n",
    "plt.title('Client-specific Convergence')\n",
    "plt.legend()\n",
    "plt.grid(True, alpha=0.3)\n",
    "plt.show()\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "## 7. Save Results\n",
    "\n",
    "Finally, let's save our trained model and results for future use.\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"python\">\n",
    "from mvkm_ed.utils import MVKMEDPersistence\n",
    "import json\n",
    "from datetime import datetime\n",
    "\n",
    "# Save the model\n",
    "save_dir = Path(\"results\")\n",
    "save_dir.mkdir(exist_ok=True)\n",
    "\n",
    "timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
    "model_path = save_dir / f\"fed_mvkm_model_{timestamp}.pkl\"\n",
    "results_path = save_dir / f\"fed_mvkm_results_{timestamp}.json\"\n",
    "\n",
    "# Save model\n",
    "MVKMEDPersistence.save_model(model, str(model_path))\n",
    "\n",
    "# Save metrics and parameters\n",
    "results = {\n",
    "    \"metrics\": metrics,\n",
    "    \"parameters\": {\n",
    "        \"n_clients\": n_clients,\n",
    "        \"n_clusters\": config.cluster_num,\n",
    "        \"n_views\": config.points_view,\n",
    "        \"alpha\": config.alpha,\n",
    "        \"gamma\": config.gamma,\n",
    "    },\n",
    "    \"timestamp\": timestamp\n",
    "}\n",
    "\n",
    "with open(results_path, 'w') as f:\n",
    "    json.dump(results, f, indent=2)\n",
    "\n",
    "print(f\"Model saved to: {model_path}\")\n",
    "print(f\"Results saved to: {results_path}\")\n",
    "</VSCode.Cell>\n",
    "\n",
    "<VSCode.Cell language=\"markdown\">\n",
    "## Conclusion\n",
    "\n",
    "This notebook demonstrated the complete workflow of using the Fed-MVKM algorithm on the DHA dataset:\n",
    "1. Data loading and preprocessing\n",
    "2. Client data distribution\n",
    "3. Model configuration and training\n",
    "4. Results analysis and visualization\n",
    "5. Model persistence\n",
    "\n",
    "The algorithm successfully performed federated multi-view clustering while preserving data privacy across clients.\n",
    "</VSCode.Cell>"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
